#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
异常处理器

该模块提供了异常处理的装饰器、上下文管理器和工具函数，用于简化异常处理流程，
提高代码的可读性和可维护性。

创建时间: 2023-07-01
作者: Python Tools Team
"""

import functools
import time
import traceback
from typing import Callable, Type, Union, Optional, List, Dict, Any, TypeVar, cast
from contextlib import contextmanager

from modules.logger import LogManager
from .exception_types import BaseAppException

# 类型变量定义
T = TypeVar('T')  # 函数返回值类型
F = TypeVar('F', bound=Callable[..., Any])  # 被装饰的函数类型
ExceptionType = Union[Type[Exception], List[Type[Exception]]]  # 异常类型或异常类型列表

# 默认日志记录器
_default_logger = LogManager.get_both_logger(name="exception_handler")


def exception_handler(
        exception_type: ExceptionType = Exception,
        reraise: bool = True,
        reraise_as: Optional[Type[Exception]] = None,
        log_level: str = "ERROR",
        log_message: Optional[str] = None,
        default_return: Any = None,
        logger=None
) -> Callable[[F], F]:
    """
    异常处理装饰器，用于捕获和处理函数执行过程中的异常
    
    Args:
        exception_type: 要捕获的异常类型或异常类型列表
        reraise: 是否重新抛出异常
        reraise_as: 重新抛出的异常类型，如果为None则使用原始异常
        log_level: 日志级别，可选值：DEBUG, INFO, WARNING, ERROR, CRITICAL
        log_message: 日志消息模板，可以使用 {exc} 引用异常对象，{func} 引用函数名
        default_return: 发生异常时的默认返回值
        logger: 自定义日志记录器，如果为None则使用默认日志记录器
    
    Returns:
        Callable: 装饰后的函数
    
    Examples:
        >>> @exception_handler(exception_type=ValueError, reraise=False, default_return=0)
        >>> def divide(a, b):
        >>>     return a / b
        >>> 
        >>> # 不会抛出异常，而是返回默认值0
        >>> result = divide(10, 0)  # 返回 0
    """
    def decorator(func: F) -> F:
        @functools.wraps(func)
        def wrapper(*args: Any, **kwargs: Any) -> Any:
            nonlocal logger
            if logger is None:
                logger = _default_logger
            
            try:
                return func(*args, **kwargs)
            except exception_type as e:
                # 构建日志消息
                msg = log_message or f"函数 {func.__name__} 执行异常: {str(e)}"
                msg = msg.format(exc=e, func=func.__name__)
                
                # 记录日志
                log_method = getattr(logger, log_level.lower(), logger.error)
                log_method(msg, exc_info=True)
                
                # 处理异常
                if reraise:
                    if reraise_as is not None:
                        if issubclass(reraise_as, BaseAppException):
                            raise reraise_as(message=str(e), cause=e)
                        else:
                            raise reraise_as(str(e)) from e
                    else:
                        raise
                
                return default_return
        
        return cast(F, wrapper)
    
    return decorator


def retry_on_exception(
        max_attempts: int = 3,
        delay: float = 1.0,
        backoff_factor: float = 2.0,
        exception_type: ExceptionType = Exception,
        log_retries: bool = True,
        logger=None
) -> Callable[[F], F]:
    """
    重试装饰器，在发生指定异常时自动重试函数
    
    Args:
        max_attempts: 最大尝试次数
        delay: 初始延迟时间（秒）
        backoff_factor: 延迟时间的增长因子
        exception_type: 触发重试的异常类型或异常类型列表
        log_retries: 是否记录重试日志
        logger: 自定义日志记录器，如果为None则使用默认日志记录器
    
    Returns:
        Callable: 装饰后的函数
    
    Examples:
        >>> @retry_on_exception(max_attempts=3, delay=1.0, exception_type=ConnectionError)
        >>> def fetch_data(url):
        >>>     # 可能抛出ConnectionError的代码
        >>>     return requests.get(url).json()
    """
    def decorator(func: F) -> F:
        @functools.wraps(func)
        def wrapper(*args: Any, **kwargs: Any) -> Any:
            nonlocal logger
            if logger is None:
                logger = _default_logger
            
            current_delay = delay
            last_exception = None
            
            for attempt in range(1, max_attempts + 1):
                try:
                    return func(*args, **kwargs)
                except exception_type as e:
                    last_exception = e
                    
                    if attempt < max_attempts:
                        if log_retries:
                            logger.warning(
                                f"函数 {func.__name__} 执行失败 (尝试 {attempt}/{max_attempts}): {str(e)}. "
                                f"将在 {current_delay:.2f} 秒后重试."
                            )
                        
                        time.sleep(current_delay)
                        current_delay *= backoff_factor
                    else:
                        if log_retries:
                            logger.error(
                                f"函数 {func.__name__} 在 {max_attempts} 次尝试后仍然失败: {str(e)}",
                                exc_info=True
                            )
            
            # 所有重试都失败，重新抛出最后一个异常
            if last_exception:
                raise last_exception
            
            # 这里理论上不会执行到，但为了类型检查添加
            return None
        
        return cast(F, wrapper)
    
    return decorator


def log_exception(
        exception: Exception,
        log_level: str = "ERROR",
        message: Optional[str] = None,
        logger=None
) -> None:
    """
    记录异常日志的工具函数
    
    Args:
        exception: 要记录的异常对象
        log_level: 日志级别，可选值：DEBUG, INFO, WARNING, ERROR, CRITICAL
        message: 自定义日志消息，如果为None则使用异常的字符串表示
        logger: 自定义日志记录器，如果为None则使用默认日志记录器
    
    Examples:
        >>> try:
        >>>     # 可能抛出异常的代码
        >>>     result = 1 / 0
        >>> except Exception as e:
        >>>     log_exception(e, message="计算过程中发生错误")
    """
    if logger is None:
        logger = _default_logger
    
    msg = message or str(exception)
    log_method = getattr(logger, log_level.lower(), logger.error)
    
    if isinstance(exception, BaseAppException):
        log_method(f"{msg} | 代码: {exception.code}", exc_info=True)
    else:
        log_method(msg, exc_info=True)


def handle_exception(
        exception: Exception,
        reraise: bool = True,
        reraise_as: Optional[Type[Exception]] = None,
        log_level: str = "ERROR",
        log_message: Optional[str] = None,
        logger=None
) -> None:
    """
    异常处理工具函数，用于记录和可选地重新抛出异常
    
    Args:
        exception: 要处理的异常对象
        reraise: 是否重新抛出异常
        reraise_as: 重新抛出的异常类型，如果为None则使用原始异常
        log_level: 日志级别，可选值：DEBUG, INFO, WARNING, ERROR, CRITICAL
        log_message: 自定义日志消息，如果为None则使用异常的字符串表示
        logger: 自定义日志记录器，如果为None则使用默认日志记录器
    
    Examples:
        >>> try:
        >>>     # 可能抛出异常的代码
        >>>     result = 1 / 0
        >>> except Exception as e:
        >>>     handle_exception(e, reraise=False, log_message="计算失败")
    """
    # 记录异常日志
    log_exception(exception, log_level, log_message, logger)
    
    # 重新抛出异常（如果需要）
    if reraise:
        if reraise_as is not None:
            if issubclass(reraise_as, BaseAppException):
                raise reraise_as(message=str(exception), cause=exception)
            else:
                raise reraise_as(str(exception)) from exception
        else:
            raise


class ExceptionContext:
    """
    异常处理上下文管理器，用于with语句中的异常处理
    
    Examples:
        >>> with ExceptionContext(exception_type=ValueError, reraise=False, log_message="操作失败"):
        >>>     # 可能抛出异常的代码
        >>>     result = 1 / 0
    """
    
    def __init__(
            self,
            exception_type: ExceptionType = Exception,
            reraise: bool = True,
            reraise_as: Optional[Type[Exception]] = None,
            log_level: str = "ERROR",
            log_message: Optional[str] = None,
            logger=None
    ):
        """
        初始化异常上下文管理器
        
        Args:
            exception_type: 要捕获的异常类型或异常类型列表
            reraise: 是否重新抛出异常
            reraise_as: 重新抛出的异常类型，如果为None则使用原始异常
            log_level: 日志级别，可选值：DEBUG, INFO, WARNING, ERROR, CRITICAL
            log_message: 日志消息模板
            logger: 自定义日志记录器，如果为None则使用默认日志记录器
        """
        self.exception_type = exception_type
        self.reraise = reraise
        self.reraise_as = reraise_as
        self.log_level = log_level
        self.log_message = log_message
        self.logger = logger or _default_logger
    
    def __enter__(self):
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type is not None and issubclass(exc_type, self.exception_type):
            # 构建日志消息
            msg = self.log_message or f"执行过程中发生异常: {str(exc_val)}"
            
            # 记录日志
            log_method = getattr(self.logger, self.log_level.lower(), self.logger.error)
            log_method(msg, exc_info=(exc_type, exc_val, exc_tb))
            
            # 处理异常
            if self.reraise:
                if self.reraise_as is not None:
                    if issubclass(self.reraise_as, BaseAppException):
                        raise self.reraise_as(message=str(exc_val), cause=exc_val)
                    else:
                        raise self.reraise_as(str(exc_val)) from exc_val
                return False  # 不抑制原始异常
            
            return True  # 抑制异常
        
        return False  # 不是我们要处理的异常类型，不抑制异常


@contextmanager
def exception_context(
        exception_type: ExceptionType = Exception,
        reraise: bool = True,
        reraise_as: Optional[Type[Exception]] = None,
        log_level: str = "ERROR",
        log_message: Optional[str] = None,
        logger=None
):
    """
    异常处理上下文管理器函数，用于with语句中的异常处理
    
    Args:
        exception_type: 要捕获的异常类型或异常类型列表
        reraise: 是否重新抛出异常
        reraise_as: 重新抛出的异常类型，如果为None则使用原始异常
        log_level: 日志级别，可选值：DEBUG, INFO, WARNING, ERROR, CRITICAL
        log_message: 日志消息模板
        logger: 自定义日志记录器，如果为None则使用默认日志记录器
    
    Examples:
        >>> with exception_context(exception_type=ValueError, reraise=False, log_message="操作失败"):
        >>>     # 可能抛出异常的代码
        >>>     result = 1 / 0
    """
    try:
        yield
    except exception_type as e:
        # 使用传入的logger或默认logger
        current_logger = logger or _default_logger
        
        # 构建日志消息
        msg = log_message or f"执行过程中发生异常: {str(e)}"
        
        # 记录日志
        log_method = getattr(current_logger, log_level.lower(), current_logger.error)
        log_method(msg, exc_info=True)
        
        # 处理异常
        if reraise:
            if reraise_as is not None:
                if issubclass(reraise_as, BaseAppException):
                    raise reraise_as(message=str(e), cause=e)
                else:
                    raise reraise_as(str(e)) from e
            else:
                raise